package com.google.code.cisx.servlets;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStreamReader;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;
import javax.servlet.Servlet;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
 * Servlet implementation class DevelopmentServlet
 */
public abstract class DevelopmentServlet extends HttpServlet {

  private static final Log log = LogFactory.getLog(DevelopmentServlet.class);

  private static final long serialVersionUID = 4018052006709541754L;

  private static final String PARAM_INIT_SQL_SCRIPTS = "init-sql-scripts";

  private static final String PARAM_DESTROY_SQL_SCRIPTS = "destroy-sql-scripts";

  private EntityManagerFactory emf;

  private EntityManager em;

  private String[] initSqlScripts;

  /**
   * @see Servlet#init(ServletConfig)
   */
  public void init(ServletConfig config) throws ServletException {
    emf = Persistence.createEntityManagerFactory("cisx-store");
    em = emf.createEntityManager();

    initSqlScripts = config.getInitParameter(PARAM_INIT_SQL_SCRIPTS).split(",");

    for (int i = 0; i < initSqlScripts.length; i++) {
      String realPath = config.getServletContext().getRealPath("/");
      loadSqlSource(new File(realPath + "WEB-INF/" + initSqlScripts[i]));
    }

  }

  public void loadSqlSource(File sqlResource) {
    try {
      log.info("Loading sql: " + sqlResource.getAbsolutePath());
      List<String> statements = new ArrayList<String>(20);
      BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(sqlResource)));
      String line = null;
      StringBuffer currentStatement = new StringBuffer();
      while ((line = br.readLine()) != null) {
        if (line.trim().length() == 0)
          continue;
        if (line.trim().startsWith("#"))
          continue;

        currentStatement.append(line);
        if (line.endsWith(";")) {
          statements.add(currentStatement.toString());
          currentStatement = new StringBuffer();
        }
      }
      // append a line if missing a ';'
      if (currentStatement.length() > 0) {
        statements.add(currentStatement.toString());
      }
      runSQLStatements(statements);
    } catch (Throwable e) {
      if (log.isErrorEnabled())
        log.error("Problem executing SQL!", e);
    }
  }

  /**
   * Run a bunch of SQL statements.
   * 
   * @param statements
   *          Statements to run.
   * @throws SQLException
   */
  public void runSQLStatements(final List<String> statements) throws SQLException {
    if (statements.size() > 0)
      log.info("Importing SQL statments:");

    for (String qry : statements) {
      Connection con = getConnection();
      try {
        Statement stmt = con.createStatement();
        stmt.execute(qry);
        con.commit();
      } catch (SQLException e) {
        try {
          con.rollback();
        } catch (SQLException e1) {
          if (log.isErrorEnabled())
            log.error("Unable to rollback transaction.");
          throw e1;
        }
        throw e;
      }
    }
    if (statements.size() > 0)
      log.info("SQL import done!");
  }

  /**
   * For use by extensions to this class to enable them to return a {@link Connection} instance.
   * 
   * @return
   * @see #getConnection()
   */
  EntityManager getEntityManager() {
    return this.em;
  }

  /**
   * Extensions use JPA Persistence Provider specific APIs and return a {@link Connection} instance.
   * 
   * @return {@link Connection} instance.
   * @see #getEntityManager()
   */
  abstract Connection getConnection();

}
